package edu.whu.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

@Aspect
@Component
public class StopWatchAspect {

    @Pointcut("@within(org.springframework.stereotype.Service)")
    public void ServicePointCut(){}
    /**
     * 时间统计结果表， key是方法的signature，value是总耗时
     */
    final Map<String,Long> metrics= Collections.synchronizedMap(new HashMap<>());

    //统计调用次数
    final Map<String,Long> nums = Collections.synchronizedMap(new HashMap<>());

    //统计最长响应时间
    final Map<String,Long> maxTime = Collections.synchronizedMap(new HashMap<>());

    //统计最短响应时间
    final Map<String,Long> minTime = Collections.synchronizedMap(new HashMap<>());

    //统计平均响应时间
    final Map<String,Long> aveTime = Collections.synchronizedMap(new HashMap<>());

    //统计发生异常的次数
    final Map<String,Long> exNum = Collections.synchronizedMap(new HashMap<>());
    /**
     * 所有标注了@Service的类的方法都会被统计，统计结果写入结果表
     */
    @Around("ServicePointCut()")
    public void caculateTime(ProceedingJoinPoint joinPoint) throws Throwable {
        long t1= Calendar.getInstance().getTimeInMillis();
        joinPoint.proceed();
        long t2= Calendar.getInstance().getTimeInMillis();
        String methodSig=joinPoint.getSignature().toString();
        long base= metrics.containsKey(methodSig)?metrics.get(methodSig):0;
        metrics.put(methodSig,base+t2-t1);
        //获取最大值
        if(base!=0){
            long maxtime = Math.max(base,(t2-t1));
            maxTime.put(methodSig,maxtime);
        }else{
            maxTime.put(methodSig,t2-t1);
        }
        //获取最小值
        if(base!=0){
            long mintime = Math.min(base,(t2-t1));
            minTime.put(methodSig,mintime);
        }else{
            minTime.put(methodSig,t2-t1);
        }
        //获取平均值
        if(base!=0){
            long avetime =( base+t2-t1)/2;
            aveTime.put(methodSig,avetime);
        }else{
            aveTime.put(methodSig,t2-t1);
        }
    }
    @AfterThrowing(pointcut= "ServicePointCut()",throwing="ex")
    public void caculateExNum(JoinPoint jp, Exception ex) {
        String methodSig=jp.getSignature().toString();
        long base= exNum.containsKey(methodSig)?exNum.get(methodSig):1;
        exNum.put(methodSig,base);
    }
    @After("ServicePointCut()")
    public void cacalateNums(JoinPoint jp) {
        String methodSig=jp.getSignature().toString();
        long base= nums.containsKey(methodSig)?nums.get(methodSig)+1:1;
        nums.put(methodSig,base);
    }


    public Map<String,Long> getNums(){return nums;}

    public Map<String,Long> getExNum(){return exNum;}

    public Map<String, Long> getMetrics() {
        return metrics;
    }

    public Map<String,Long> getMaxTime(){return maxTime;}

    public Map<String,Long> getMinTime(){return minTime;}

    public Map<String,Long> getAveTime(){return aveTime;}
}
